/**
* Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
/*
* Created on May 24, 2005
*
* @author Fabio Zadrozny
*/
package org.python.pydev.editor.codecompletion.revisited;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.python.pydev.core.DeltaSaver;
import org.python.pydev.core.ICodeCompletionASTManager;
import org.python.pydev.core.IInterpreterInfo;
import org.python.pydev.core.IInterpreterManager;
import org.python.pydev.core.IModule;
import org.python.pydev.core.IModulesManager;
import org.python.pydev.core.IProjectModulesManager;
import org.python.pydev.core.IPythonNature;
import org.python.pydev.core.IPythonPathNature;
import org.python.pydev.core.ISystemModulesManager;
import org.python.pydev.core.ModulesKey;
import org.python.pydev.core.docutils.StringUtils;
import org.python.pydev.core.log.Log;
import org.python.pydev.editor.codecompletion.revisited.javaintegration.JavaProjectModulesManagerCreator;
import org.python.pydev.plugin.nature.PythonNature;
import com.aptana.shared_core.io.FileUtils;
import com.aptana.shared_core.structure.Tuple;
/**
* @author Fabio Zadrozny
*/
public final class ProjectModulesManager extends ModulesManagerWithBuild implements IProjectModulesManager {
private static final boolean DEBUG_MODULES = false;
//these attributes must be set whenever this class is restored.
private volatile IProject project;
private volatile IPythonNature nature;
public ProjectModulesManager() {
}
/**
* @see org.python.pydev.core.IProjectModulesManager#setProject(org.eclipse.core.resources.IProject, boolean)
*/
public void setProject(IProject project, IPythonNature nature, boolean restoreDeltas) {
this.project = project;
this.nature = nature;
File completionsCacheDir = this.nature.getCompletionsCacheDir();
if (completionsCacheDir == null) {
return; //project was deleted.
}
this.deltaSaver = new DeltaSaver<ModulesKey>(completionsCacheDir, "v1_astdelta", readFromFileMethod,
toFileMethod);
if (!restoreDeltas) {
deltaSaver.clearAll(); //remove any existing deltas
} else {
deltaSaver.processDeltas(this); //process the current deltas (clears current deltas automatically and saves it when the processing is concluded)
}
}
// ------------------------ delta processing
/**
* @see org.python.pydev.core.IProjectModulesManager#endProcessing()
*/
public void endProcessing() {
//save it with the updated info
nature.saveAstManager();
}
// ------------------------ end delta processing
/**
* @see org.python.pydev.core.IProjectModulesManager#setPythonNature(org.python.pydev.core.IPythonNature)
*/
public void setPythonNature(IPythonNature nature) {
this.nature = nature;
}
/**
* @see org.python.pydev.core.IProjectModulesManager#getNature()
*/
public IPythonNature getNature() {
return nature;
}
/**
* @param defaultSelectedInterpreter
* @see org.python.pydev.core.IProjectModulesManager#getSystemModulesManager()
*/
public ISystemModulesManager getSystemModulesManager() {
if (nature == null) {
Log.log("Nature still not set");
return null; //still not set (initialization)
}
try {
return nature.getProjectInterpreter().getModulesManager();
} catch (Exception e1) {
return null;
}
}
/**
* @see org.python.pydev.core.IProjectModulesManager#getAllModuleNames(boolean addDependencies, String partStartingWithLowerCase)
*/
public Set<String> getAllModuleNames(boolean addDependencies, String partStartingWithLowerCase) {
if (addDependencies) {
Set<String> s = new HashSet<String>();
IModulesManager[] managersInvolved = this.getManagersInvolved(true);
for (int i = 0; i < managersInvolved.length; i++) {
s.addAll(managersInvolved[i].getAllModuleNames(false, partStartingWithLowerCase));
}
return s;
} else {
return super.getAllModuleNames(addDependencies, partStartingWithLowerCase);
}
}
/**
* @return all the modules that start with some token (from this manager and others involved)
*/
@Override
public SortedMap<ModulesKey, ModulesKey> getAllModulesStartingWith(String strStartingWith) {
SortedMap<ModulesKey, ModulesKey> ret = new TreeMap<ModulesKey, ModulesKey>();
IModulesManager[] managersInvolved = this.getManagersInvolved(true);
for (int i = 0; i < managersInvolved.length; i++) {
ret.putAll(managersInvolved[i].getAllDirectModulesStartingWith(strStartingWith));
}
return ret;
}
/**
* @see org.python.pydev.core.IProjectModulesManager#getModule(java.lang.String, org.python.pydev.plugin.nature.PythonNature, boolean)
*/
public IModule getModule(String name, IPythonNature nature, boolean dontSearchInit) {
return getModule(name, nature, true, dontSearchInit);
}
/**
* When looking for relative, we do not check dependencies
*/
public IModule getRelativeModule(String name, IPythonNature nature) {
return super.getModule(false, name, nature, true); //cannot be a compiled module
}
/**
* @see org.python.pydev.core.IProjectModulesManager#getModule(java.lang.String, org.python.pydev.plugin.nature.PythonNature, boolean, boolean)
*/
public IModule getModule(String name, IPythonNature nature, boolean checkSystemManager, boolean dontSearchInit) {
Tuple<IModule, IModulesManager> ret = getModuleAndRelatedModulesManager(name, nature, checkSystemManager,
dontSearchInit);
if (ret != null) {
return ret.o1;
}
return null;
}
/**
* @return a tuple with the IModule requested and the IModulesManager that contained that module.
*/
public Tuple<IModule, IModulesManager> getModuleAndRelatedModulesManager(String name, IPythonNature nature,
boolean checkSystemManager, boolean dontSearchInit) {
IModule module = null;
IModulesManager[] managersInvolved = this.getManagersInvolved(true); //only get the system manager here (to avoid recursion)
for (IModulesManager m : managersInvolved) {
if (m instanceof ISystemModulesManager) {
module = ((ISystemModulesManager) m).getBuiltinModule(name, dontSearchInit);
if (module != null) {
if (DEBUG_MODULES) {
System.out.println("Trying to get:" + name + " - " + " returned builtin:" + module + " - "
+ m.getClass());
}
return new Tuple<IModule, IModulesManager>(module, m);
}
}
}
for (IModulesManager m : managersInvolved) {
if (m instanceof IProjectModulesManager) {
IProjectModulesManager pM = (IProjectModulesManager) m;
module = pM.getModuleInDirectManager(name, nature, dontSearchInit);
} else if (m instanceof ISystemModulesManager) {
ISystemModulesManager systemModulesManager = (ISystemModulesManager) m;
module = systemModulesManager.getModuleWithoutBuiltins(name, nature, dontSearchInit);
} else {
throw new RuntimeException("Unexpected: " + m);
}
if (module != null) {
if (DEBUG_MODULES) {
System.out.println("Trying to get:" + name + " - " + " returned:" + module + " - " + m.getClass());
}
return new Tuple<IModule, IModulesManager>(module, m);
}
}
if (DEBUG_MODULES) {
System.out.println("Trying to get:" + name + " - " + " returned:null - " + this.getClass());
}
return null;
}
/**
* Only searches the modules contained in the direct modules manager.
*/
public IModule getModuleInDirectManager(String name, IPythonNature nature, boolean dontSearchInit) {
return super.getModule(name, nature, dontSearchInit);
}
protected String getResolveModuleErr(IResource member) {
return "Unable to find the path " + member + " in the project were it\n"
+ "is added as a source folder for pydev (project: " + project.getName() + ")";
}
public String resolveModuleOnlyInProjectSources(String fileAbsolutePath, boolean addExternal) throws CoreException {
String onlyProjectPythonPathStr = this.nature.getPythonPathNature().getOnlyProjectPythonPathStr(addExternal);
HashSet<String> projectSourcePath = new HashSet<String>(StringUtils.splitAndRemoveEmptyTrimmed(
onlyProjectPythonPathStr, '|'));
return this.pythonPathHelper.resolveModule(fileAbsolutePath, new ArrayList<String>(projectSourcePath));
}
/**
* @see org.python.pydev.core.IProjectModulesManager#resolveModule(java.lang.String)
*/
public String resolveModule(String full) {
return resolveModule(full, true);
}
/**
* @see org.python.pydev.core.IProjectModulesManager#resolveModule(java.lang.String, boolean)
*/
public String resolveModule(String full, boolean checkSystemManager) {
IModulesManager[] managersInvolved = this.getManagersInvolved(checkSystemManager);
for (IModulesManager m : managersInvolved) {
String mod;
if (m instanceof IProjectModulesManager) {
IProjectModulesManager pM = (IProjectModulesManager) m;
mod = pM.resolveModuleInDirectManager(full);
} else {
mod = m.resolveModule(full);
}
if (mod != null) {
return mod;
}
}
return null;
}
public String resolveModuleInDirectManager(String full) {
return super.resolveModule(full);
}
public String resolveModuleInDirectManager(IFile member) {
File inOs = member.getRawLocation().toFile();
return resolveModuleInDirectManager(FileUtils.getFileAbsolutePath(inOs));
}
/**
* @see org.python.pydev.core.IProjectModulesManager#getSize(boolean)
*/
public int getSize(boolean addDependenciesSize) {
if (addDependenciesSize) {
int size = 0;
IModulesManager[] managersInvolved = this.getManagersInvolved(true);
for (int i = 0; i < managersInvolved.length; i++) {
size += managersInvolved[i].getSize(false);
}
return size;
} else {
return super.getSize(addDependenciesSize);
}
}
/**
* @see org.python.pydev.core.IProjectModulesManager#getBuiltins()
*/
public String[] getBuiltins() {
String[] builtins = null;
ISystemModulesManager systemModulesManager = getSystemModulesManager();
if (systemModulesManager != null) {
builtins = systemModulesManager.getBuiltins();
}
return builtins;
}
/**
* @param checkSystemManager whether the system manager should be added
* @param referenced true if we should get the referenced projects
* false if we should get the referencing projects
* @return the Managers that this project references or the ones that reference this project (depends on 'referenced')
*
* Change in 1.3.3: adds itself to the list of returned managers
*/
private synchronized IModulesManager[] getManagers(boolean checkSystemManager, boolean referenced) {
CompletionCache localCompletionCache = this.completionCache;
if (localCompletionCache != null) {
IModulesManager[] ret = localCompletionCache.getManagers(referenced);
if (ret != null) {
return ret;
}
}
ArrayList<IModulesManager> list = new ArrayList<IModulesManager>();
ISystemModulesManager systemModulesManager = getSystemModulesManager();
//add itself 1st
list.add(this);
//get the projects 1st
if (project != null) {
IModulesManager javaModulesManagerForProject = JavaProjectModulesManagerCreator
.createJavaProjectModulesManagerIfPossible(project);
if (javaModulesManagerForProject != null) {
list.add(javaModulesManagerForProject);
}
Set<IProject> projs;
if (referenced) {
projs = getReferencedProjects(project);
} else {
projs = getReferencingProjects(project);
}
addModuleManagers(list, projs);
}
//the system is the last one we add
//http://sourceforge.net/tracker/index.php?func=detail&aid=1687018&group_id=85796&atid=577329
if (checkSystemManager && systemModulesManager != null) {
//may be null in initialization or if the project does not have a related interpreter manager at the present time
//(i.e.: misconfigured project)
list.add(systemModulesManager);
}
IModulesManager[] ret = (IModulesManager[]) list.toArray(new IModulesManager[list.size()]);
if (localCompletionCache != null) {
localCompletionCache.setManagers(ret, referenced);
}
return ret;
}
public static Set<IProject> getReferencingProjects(IProject project) {
HashSet<IProject> memo = new HashSet<IProject>();
getProjectsRecursively(project, false, memo);
memo.remove(project); //shouldn't happen unless we've a cycle...
return memo;
}
public static Set<IProject> getReferencedProjects(IProject project) {
HashSet<IProject> memo = new HashSet<IProject>();
getProjectsRecursively(project, true, memo);
memo.remove(project); //shouldn't happen unless we've a cycle...
return memo;
}
/**
* @param project the project for which we want references.
* @param referenced whether we want to get the referenced projects or the ones referencing this one.
* @param memo (out) this is the place where all the projects will e available.
*
* Note: the project itself will not be added.
*/
private static void getProjectsRecursively(IProject project, boolean referenced, HashSet<IProject> memo) {
IProject[] projects = null;
try {
if (project == null || !project.isOpen() || !project.exists() || memo.contains(projects)) {
return;
}
if (referenced) {
projects = project.getReferencedProjects();
} else {
projects = project.getReferencingProjects();
}
} catch (CoreException e) {
//ignore (it's closed)
}
if (projects != null) {
for (IProject p : projects) {
if (!memo.contains(p)) {
memo.add(p);
getProjectsRecursively(p, referenced, memo);
}
}
}
}
/**
* @param list the list that will be filled with the managers
* @param projects the projects that should have the managers added
*/
private void addModuleManagers(ArrayList<IModulesManager> list, Collection<IProject> projects) {
for (IProject project : projects) {
PythonNature nature = PythonNature.getPythonNature(project);
if (nature != null) {
ICodeCompletionASTManager otherProjectAstManager = nature.getAstManager();
if (otherProjectAstManager != null) {
IModulesManager projectModulesManager = otherProjectAstManager.getModulesManager();
if (projectModulesManager != null) {
list.add((IModulesManager) projectModulesManager);
}
} else {
String msg = "No ast manager configured for :" + project.getName();
Log.log(IStatus.WARNING, msg, new RuntimeException(msg));
}
}
IModulesManager javaModulesManagerForProject = JavaProjectModulesManagerCreator
.createJavaProjectModulesManagerIfPossible(project);
if (javaModulesManagerForProject != null) {
list.add(javaModulesManagerForProject);
}
}
}
/**
* @return Returns the managers that this project references(does not include itself).
*/
public IModulesManager[] getManagersInvolved(boolean checkSystemManager) {
return getManagers(checkSystemManager, true);
}
/**
* @return Returns the managers that reference this project (does not include itself).
*/
public IModulesManager[] getRefencingManagersInvolved(boolean checkSystemManager) {
return getManagers(checkSystemManager, false);
}
/**
* Helper to work as a timer to know when to check for pythonpath consistencies.
*/
private volatile long checkedPythonpathConsistency = 0;
/**
* @see org.python.pydev.core.IProjectModulesManager#getCompletePythonPath()
*/
public List<String> getCompletePythonPath(IInterpreterInfo interpreter, IInterpreterManager manager) {
List<String> l = new ArrayList<String>();
IModulesManager[] managersInvolved = getManagersInvolved(true);
for (IModulesManager m : managersInvolved) {
if (m instanceof ISystemModulesManager) {
ISystemModulesManager systemModulesManager = (ISystemModulesManager) m;
l.addAll(systemModulesManager.getCompletePythonPath(interpreter, manager));
} else {
PythonPathHelper h = (PythonPathHelper) m.getPythonPathHelper();
if (h != null) {
List<String> pythonpath = h.getPythonpath();
//Note: this was previously only l.addAll(pythonpath), and was changed to the code below as a place
//to check for consistencies in the pythonpath stored in the pythonpath helper and the pythonpath
//available in the PythonPathNature (in general, when requesting it the PythonPathHelper should be
//used, as it's a cache for the resolved values of the PythonPathNature).
boolean forceCheck = false;
ProjectModulesManager m2 = null;
String onlyProjectPythonPathStr = null;
if (m instanceof ProjectModulesManager) {
long currentTimeMillis = System.currentTimeMillis();
m2 = (ProjectModulesManager) m;
//check at most once every 20 seconds (or every time if the pythonpath is empty... in which case
//it should be fast to get it too if it's consistent).
if (pythonpath.size() == 0 || currentTimeMillis - m2.checkedPythonpathConsistency > 20 * 1000) {
try {
IPythonNature n = m.getNature();
if (n != null) {
IPythonPathNature pythonPathNature = n.getPythonPathNature();
if (pythonPathNature != null) {
onlyProjectPythonPathStr = pythonPathNature.getOnlyProjectPythonPathStr(true);
m2.checkedPythonpathConsistency = currentTimeMillis;
forceCheck = true;
}
}
} catch (Exception e) {
Log.log(e);
}
}
}
if (forceCheck) {
//Check if it's actually correct and auto-fix if it's not.
List<String> parsed = PythonPathHelper.parsePythonPathFromStr(onlyProjectPythonPathStr, null);
if (m2.nature != null && !new HashSet<String>(parsed).equals(new HashSet<String>(pythonpath))) {
// Make it right at this moment (so any other place that calls it before the restore
//takes place has the proper version).
h.setPythonPath(parsed);
// Force a rebuild as the PythonPathHelper paths are not up to date.
m2.nature.rebuildPath();
}
l.addAll(parsed); //add the proper paths
} else {
l.addAll(pythonpath);
}
}
}
}
return l;
}
}